`include "common.svh"
import "DPI-C" function void commit_ebreak();
module commit #(
    parameter COMMIT_WIDTH = 2
) (
    input clk,
    input rst,

    input i_valid,
    input i_op_valid[COMMIT_WIDTH-1:0],
    input ROB_Entry i_op[COMMIT_WIDTH-1:0]  /* verilator public */,
    input ROB_State i_op_state[COMMIT_WIDTH-1:0]  /* verilator public */,
    input i_restore_gc[COMMIT_WIDTH-1:0],
    output i_ready,

    output o_valid[COMMIT_WIDTH-1:0],
    output COMMIT_TO_RENAME o_data[COMMIT_WIDTH-1:0],
    input o_ready,
    output csr_commit_valid[COMMIT_WIDTH-1:0]
);

  always_ff @(posedge clk) begin
    integer i;
    if (!rst) begin
      if (i_valid & i_ready) begin
        for (i = 0; i < COMMIT_WIDTH; i = i + 1) begin
          if (i_op_valid[i]) begin
            // $display("Commit PC 0x%08x", i_op[i].rop.fop.pc);
            if(i_op[i].rop.fop.dop.fu_type == FU_MISC && i_op[i].rop.fop.dop.fu_op == MISC_EBREAK) begin
              commit_ebreak();
            end
          end
        end
      end
    end
  end

  assign i_ready = o_ready;

  genvar gi;
  generate
    for (gi = 0; gi < COMMIT_WIDTH; gi = gi + 1) begin : CommitGen
      assign o_valid[gi] = i_valid & i_op_valid[gi];
      assign o_data[gi].has_gc = i_op[gi].rop.has_gc;
      assign o_data[gi].has_rd = i_op[gi].rop.fop.dop.has_rd;
      assign o_data[gi].free_entry = i_op[gi].rop.free_pdst;
      assign o_data[gi].restore_gc = i_restore_gc[gi];
      assign o_data[gi].restore_freelist_rptr = i_op[gi].rop.freelist_rptr;
      assign csr_commit_valid[gi] = o_valid[gi] & o_ready;
    end
  endgenerate

  int commit_branch_total_inc;
  int commit_branch_total_miss_inc;
  int commit_branch_cond_inc;
  int commit_branch_cond_miss_inc;
  int commit_branch_jump_inc;
  int commit_branch_jump_miss_inc;
  int commit_branch_indirect_jump_inc;
  int commit_branch_indirect_jump_miss_inc;
  int commit_branch_call_inc;
  int commit_branch_call_miss_inc;
  int commit_branch_return_inc;
  int commit_branch_return_miss_inc;

  `PERF_EVENT_MULT(commit_branch_total, commit_branch_total_inc, 32);
  `PERF_EVENT_MULT(commit_branch_total_miss, commit_branch_total_miss_inc, 32);
  `PERF_EVENT_MULT(commit_branch_cond, commit_branch_cond_inc, 32);
  `PERF_EVENT_MULT(commit_branch_cond_miss, commit_branch_cond_miss_inc, 32);
  `PERF_EVENT_MULT(commit_branch_jump, commit_branch_jump_inc, 32);
  `PERF_EVENT_MULT(commit_branch_jump_miss, commit_branch_jump_miss_inc, 32);
  `PERF_EVENT_MULT(commit_branch_indirect_jump, commit_branch_indirect_jump_inc, 32);
  `PERF_EVENT_MULT(commit_branch_indirect_jump_miss, commit_branch_indirect_jump_miss_inc, 32);
  `PERF_EVENT_MULT(commit_branch_call, commit_branch_call_inc, 32);
  `PERF_EVENT_MULT(commit_branch_call_miss, commit_branch_call_miss_inc, 32);
  `PERF_EVENT_MULT(commit_branch_return, commit_branch_return_inc, 32);
  `PERF_EVENT_MULT(commit_branch_return_miss, commit_branch_return_miss_inc, 32);
  always_comb begin
    integer idx;
    commit_branch_total_inc = 'b0;
    commit_branch_total_miss_inc = 'b0;
    commit_branch_cond_inc = 'b0;
    commit_branch_cond_miss_inc = 'b0;
    commit_branch_jump_inc = 'b0;
    commit_branch_jump_miss_inc = 'b0;
    commit_branch_call_inc = 'b0;
    commit_branch_call_miss_inc = 'b0;
    commit_branch_return_inc = 'b0;
    commit_branch_return_miss_inc = 'b0;
    commit_branch_indirect_jump_inc = 'b0;
    commit_branch_indirect_jump_miss_inc = 'b0;
    for (idx = 0; idx < COMMIT_WIDTH; idx = idx + 1) begin
      bit is_bru = i_valid & i_ready & i_op_valid[idx] & i_op[idx].rop.fop.dop.fu_type == FU_BRU;
      bit is_cond = is_bru && (i_op[idx].rop.fop.dop.branch_type == BT_Branch);
      bit is_jump = is_bru && (i_op[idx].rop.fop.dop.branch_type == BT_Jump);
      bit is_call = is_bru && (i_op[idx].rop.fop.dop.branch_type == BT_Call);
      bit is_ret = is_bru && (i_op[idx].rop.fop.dop.branch_type == BT_Ret);
      bit is_indirect_jump = is_jump && (i_op[idx].rop.fop.dop.fu_op == BRU_JALR);
      if (is_bru) begin
        commit_branch_total_inc = commit_branch_total_inc + 'b1;
        commit_branch_cond_inc = commit_branch_cond_inc + (is_cond ? 'b1 : 'b0);
        commit_branch_jump_inc = commit_branch_jump_inc + (is_jump ? 'b1 : 'b0);
        commit_branch_indirect_jump_inc   = commit_branch_indirect_jump_inc + (is_indirect_jump ? 'b1 : 'b0);
        commit_branch_call_inc = commit_branch_call_inc + (is_call ? 'b1 : 'b0);
        commit_branch_return_inc = commit_branch_return_inc + (is_ret ? 'b1 : 'b0);
        if (i_op_state[idx].bpred_miss) begin
          commit_branch_total_miss_inc = commit_branch_total_miss_inc + 'b1;
          commit_branch_cond_miss_inc = commit_branch_cond_miss_inc + (is_cond ? 'b1 : 'b0);
          commit_branch_jump_miss_inc = commit_branch_jump_miss_inc + (is_jump ? 'b1 : 'b0);
          commit_branch_indirect_jump_miss_inc   = commit_branch_indirect_jump_miss_inc + (is_indirect_jump ? 'b1 : 'b0);
          commit_branch_call_miss_inc = commit_branch_call_miss_inc + (is_call ? 'b1 : 'b0);
          commit_branch_return_miss_inc = commit_branch_return_miss_inc + (is_ret ? 'b1 : 'b0);
        end
      end
    end
  end
endmodule
